// Copyright 2007 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.enterprise.connector.afyd;

import com.google.gdata.client.appsforyourdomain.UserService;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.Link;
import com.google.gdata.data.appsforyourdomain.provisioning.UserFeed;
import com.google.gdata.data.appsforyourdomain.provisioning.UserEntry;
import com.google.gdata.util.ServiceException;
import com.google.gdata.util.NotModifiedException;
import com.google.gdata.util.AuthenticationException;


import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.AbstractList;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.util.Collections;
import java.util.logging.Logger;

/**
 * This class behaves like a basic List however it is really a caching layer
 * in front of the user list fetchable using the Google Apps Provisioning API.
 * 
 * The contents of this list are the plain text usernames (not email addresses)
 * arranged by the natural ordering of Strings.
 * 
 * Each call to this list that triggers a get() or size() will possibly cause
 * a refetch from the back end service to occur.  Once a refetch has occurred,
 * it will not occur again for at least a period of the number of seconds
 * specified by the "ttl" property.
 * 
 * @author amsmith@google.com (Your Name Here)
 */
public class AfydBackedUserList extends AbstractList {
  
  /** The logger for this class. */
  private static final Logger LOGGER =
      Logger.getLogger(AfydBackedUserList.class.getName());
  
  private static final String USER_LIST_FEED_PATTERN =
    "https://www.google.com/a/feeds/{domain}/user/2.0";
  
  /** The time the real user list was last fetched */
  private Date lastFetchDate;
  
  /** The actual user list */
  private List userList;
  
  /** The domain this user list is for */
  private String domain;
  
  /** The service that will be used to fetch the user list */
  private UserService service;
  
  /** The number of seconds out-of-date the user list can be */
  private int ttl = 0;
 
  
  public void setTtl(int ttl) {
    this.ttl = ttl;
  }

  public int getTtl() {
    return ttl;
  }

  public AfydBackedUserList (   String domain, 
                                String email,
                                String password,
                                UserService service)
  throws AuthenticationException {
    service.setUserCredentials(email, password);
    this.domain = domain;
    this.service = service;
  }
  
  /**
   * Determines if refetch() should be called by examining lastFetchDate and the
   * ttl fields.
   */
  private void maybeRefetch() {
    if (lastFetchDate == null) {
      refetch();
    } else {
      long then = lastFetchDate.getTime();
      long now = new Date().getTime();
      if ((now - then) / 1000.0 > ttl ) {
        refetch();
      }
    }
  }
  
  /**
   * Fetches the actual user list from the injected UserService, updates the
   * value of fetchTime
   * 
   * The value of userList is never null after calling this method.
   */
  private void refetch() {
    Date newFetchDate = new Date();
    DateTime lastFetchDateTime = null;
    if (lastFetchDate != null) {
      lastFetchDateTime = new DateTime(lastFetchDate);
    }

     
    String urlString = USER_LIST_FEED_PATTERN
                        .replaceAll("\\{domain\\}", domain);
    
    URL feedUrl = null;
    try {
      feedUrl = new URL(urlString);
    } catch (MalformedURLException murle) {
      LOGGER.severe(murle.toString());
    }
        
    UserFeed feed = null;
    try {
      Link nextLink;
      do {
        UserFeed currentPage = (UserFeed) service.getFeed(
            feedUrl, UserFeed.class, lastFetchDateTime);
        if (feed == null) {
          feed = new UserFeed();
        }
        feed.getEntries().addAll(currentPage.getEntries());
        nextLink = currentPage.getLink(Link.Rel.NEXT, Link.Type.ATOM);
      } while(nextLink != null);
    } catch (IOException ioe) {
      LOGGER.severe(ioe.toString());
    } catch (NotModifiedException nme) {
      LOGGER.info(nme.toString());
    } catch (ServiceException se) {
      LOGGER.severe(se.toString());
    }
    
    if (feed != null) {
      List results = feed.getEntries();
      userList = new ArrayList(results.size());
      for (Iterator iter = results.iterator(); iter.hasNext(); ) {
        userList.add( ((UserEntry) iter.next()).getLogin().getUserName() );
      }
      Collections.sort(userList);
    }
    
    if (userList == null)
      userList = new ArrayList(0);
    
    lastFetchDate = newFetchDate;
  }
  
  /**
   * Gets an element from the cached user list.
   */
  public Object get(int index) {
    maybeRefetch();    
    return userList.get(index);
  }
  
  /**
   * Gets the size from the cached user list.
   */
  public int size() {
    maybeRefetch();
    return userList.size();
  }
    
}
